package drds.common.db.xml_mapping.executor.statement;

import drds.common.db.xml_mapping.executor.Executor;
import drds.common.db.xml_mapping.executor.ExecutorException;
import drds.common.db.xml_mapping.executor.key_generator.PrimaryKeyGenerator;
import drds.common.db.xml_mapping.executor.parameter.ParameterHandler;
import drds.common.db.xml_mapping.executor.resultset.ResultSetHandler;
import drds.common.db.xml_mapping.mapping.BindSql;
import drds.common.db.xml_mapping.mapping.Mapper;
import drds.common.db.xml_mapping.parameter_and_result_value_type.TypeHandlerRegistry;
import drds.common.db.xml_mapping.reflection.factory.ObjectFactory;
import drds.common.db.xml_mapping.session.Configuration;
import drds.common.db.xml_mapping.session.ResultHandler;
import drds.common.db.xml_mapping.session.RowBounds;

import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;


public abstract class BaseStatementHandler implements StatementHandler {

  protected final Configuration configuration;
  protected final ObjectFactory objectFactory;
  protected final TypeHandlerRegistry typeHandlerRegistry;
  protected final ResultSetHandler resultSetHandler;
  protected final ParameterHandler parameterHandler;

  protected final Executor executor;
  protected final Mapper mappedStatement;
  protected final RowBounds rowBounds;

  protected BindSql bindSql;

  protected BaseStatementHandler(Executor executor, Mapper MappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BindSql bindSql) {
    this.configuration = MappedStatement.getConfiguration();
    this.executor = executor;
    this.mappedStatement = MappedStatement;
    this.rowBounds = rowBounds;

    this.typeHandlerRegistry = configuration.getTypeHandlerRegistry();
    this.objectFactory = configuration.getObjectFactory();

    if (bindSql == null) { //  get the key before calculating the statement
      generateKeys(parameterObject);
      bindSql = MappedStatement.getBoundSql(parameterObject);
    }

    this.bindSql = bindSql;

    this.parameterHandler = configuration.newParameterHandler(MappedStatement, parameterObject, bindSql);
    this.resultSetHandler = configuration.newResultSetHandler(executor, MappedStatement, rowBounds, parameterHandler, resultHandler, bindSql);
  }

  @Override
  public BindSql getBindSql() {
    return bindSql;
  }

  @Override
  public ParameterHandler getParameterHandler() {
    return parameterHandler;
  }

  @Override
  public Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException {
    Statement statement = null;
    try {
      statement = instantiateStatement(connection);
      return statement;
    } catch (SQLException e) {
      closeStatement(statement);
      throw e;
    } catch (Exception e) {
      closeStatement(statement);
      throw new ExecutorException("Error preparing statement.  Cause: " + e, e);
    }
  }

  protected abstract Statement instantiateStatement(Connection connection) throws SQLException;


  protected void closeStatement(Statement statement) {
    try {
      if (statement != null) {
        statement.close();
      }
    } catch (SQLException e) {
      //ignore
    }
  }

  protected void generateKeys(Object parameter) {
    PrimaryKeyGenerator primaryKeyGenerator = mappedStatement.getPrimaryKeyGenerator();

    primaryKeyGenerator.generate(executor, mappedStatement, null, parameter);

  }

}
